<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>继承</title>
</head>

<body>
    <div class="part">
        <section>
            <p>许多OO语言都支持两种继承方式：接口继承跟实现继承。</p>
            <p>由于函数没有签名，在ES中无法实现接口继承。</p>
            <p>ES只支持实现继承，而且其实现继承主要是依靠原型链来实现。</p>
        </section>
    </div>
    <div class="part">
        <h1>6.3.1 原型链</h1>
        <section>
            <p>ES中描述了原型链的概念，并将原型链作为实现继承的主要方法。</p>
            <p>其基本思想是利用原型让一个引用类型继承另一个引用类型的属性跟方法。</p>
            <dl>
                <dt>构造函数、原型跟实例的关系</dt>
                <dd>每个构造函数都有一个原型对象，原型对象包含一个指向构造函数的指针，而实例包含一个指向原型对象的内部指针。</dd>
                <dd>假如我们让原型对象等于另一个类型的实例，此时的原型对象将包含一个指向另一个原型的指针，相应地，另一个原型中也包含一个指向另一个构造函数的指针。</dd>
                <dd>假如另一个原型又是另一个类型的实例，那么上述关系依然成立，如此层层递进，就构成了实例与原型的链条。</dd>
                <dd>这就是所谓原型链的概念。</dd>
                <dd><img src="images/6.3/6.3.1_1.png" /></dd>
                <dd><img src="images/6.3/6.3.1_2.png" /></dd>
            </dl>
            <dl>
                <dt>调用instance.getSuperValue()会经历以下3个步骤</dt>
                <dd>1）搜索实例</dd>
                <dd>2）搜索SubType.prototype</dd>
                <dd>3）搜索SuperType.prototype,最后一步才找到该方法。</dd>
                <dd>在找不到属性跟方法的情况下，搜索过程总是一环一环地前行到原型链的末端才会停下来。</dd>
            </dl>
            <h2>6.3.1.1 别忘了默认原型</h2>
            <p>所有的引用类型默认都继承了Object，而这个继承是通过原型链实现的。</p>
            <p><img src="images/6.3/6.3.1_3.png" /></p>
            <p><img src="images/6.3/6.3.1_4.png" /></p>
            <dl>
                <dt>相关说明</dt>
                <dd>所有函数默认原型都是Object的实例，因此默认原型都会包含一个内部指针，指向Object.prototype</dd>
                <dd>这也正是所有自定义类型都会继承toString()、valueOf()等默认方法的根本原因。</dd>
            </dl>
            <h2>6.3.1.2 确定原型和实例的关系</h2>
            <dl>
                <dt>方式一：instanceof</dt>
                <dd>只要用这个操作符来测试实例与原型链中出现过的构造函数，结果就返回true</dd>
                <dd>由于原型链的关系，instance是Object、SuperType、SubType中任何一个类型的实例。</dd>
                <dd><img src="images/6.3/6.3.1_5.png" /></dd>
            </dl>
            <dl>
                <dt>方式二：isPrototypeOf</dt>
                <dd>只要原型链出现过的原型，都可以说是该原型链派生的实例的原型</dd>
                <dd>因此，isPrototypeOf方法也会返回true。</dd>
                <dd><img src="images/6.3/6.3.1_6.png" /></dd>
            </dl>
            <h2>6.3.1.3 谨慎地定义方法</h2>
            <dl>
                <dt>相关说明</dt>
                <dd>子类型有的时候需要覆盖超类型中的某个方法，或者需要添加超类型中不存在的某个方法。</dd>
                <dd>但不管怎样，给原型添加方法的代码一定要放在替换原型语句之后。</dd>
                <dd>在通过原型链实现继承时，不能使用对象字面量创建原型方法。因为这样会重写原型链。</dd>
                <dd><img src="images/6.3/6.3.1_7.png" /></dd>
                <dd><img src="images/6.3/6.3.1_8.png" /></dd>
            </dl>
            <h2>6.3.1.4 原型链的问题</h2>
            <dl>
                <dt>相关说明</dt>
                <dd>问题1：包含引用类型只的原型，会导致后续的实例对象共享该属性，相互影响。</dd>
                <dd>问题2：创建子类型的实例时，不能向超类型的构造函数中传递参数。</dd>
            </dl>
            <div class="actions">
                <button onclick="handlePrototypeChain()">原型链</button>
                <button onclick="handleAddFunction()">子类型增加方法</button>
                <button onclick="handleAddFunctionLiteral()">增加方法不能用对象字面量</button>
            </div>
        </section>
    </div>
    <script>
        "use strict";
        // =============6.3.1 原型链
        /**
         * handlePrototypeChain 
         */
        function handlePrototypeChain() {
            function SuperType() {
                this.property = true
            }
            SuperType.prototype.getSuperValue = function () {
                return this.property
            }
            function SubType() {
                this.subProperty = false
            }
            // 继承了SuperType
            SubType.prototype = new SuperType()
            SubType.prototype.getSubValue = function () {
                return this.subProperty
            }
            var instance = new SubType()
            console.warn("instance.getSuperType()", instance.getSuperValue(), instance)
            // instanceof
            console.warn("instance instanceof Object", instance instanceof Object)
            console.warn("instance instanceof SuperType", instance instanceof SuperType)
            console.warn("instance instanceof SubType", instance instanceof SubType)
            // isPrototypeOf
            console.warn("Object.prototype.isPrototypeOf(instance)", Object.prototype.isPrototypeOf(instance))
            console.warn("SuperType.prototype.isPrototypeOf(instance)", SuperType.prototype.isPrototypeOf(instance))
            console.warn("SubType.prototype.isPrototypeOf(instance)", SubType.prototype.isPrototypeOf(instance))
        }
        /**
         * handleAddFunction
         */
        function handleAddFunction() {
            function SuperType() {
                this.property = true
            }
            SuperType.prototype.getSuperValue = function () {
                return this.property
            }
            function SubType() {
                this.subProperty = false
            }
            // 继承了SuperType
            SubType.prototype = new SuperType()
            // 添加新方法
            SubType.prototype.getSubValue = function () {
                return this.subProperty
            }
            // 重新超类型的方法
            SubType.prototype.getSuperValue = function () {
                return false
            }
            var instance = new SubType()
            console.warn("instance.getSuperType()", instance.getSuperValue())
        }
        /**
         * handleAddFunctionLiteral
         */
        function handleAddFunctionLiteral() {
            function SuperType() {
                this.property = true
            }
            SuperType.prototype.getSuperValue = function () {
                return this.property
            }
            function SubType() {
                this.subProperty = false
            }
            // 继承了SuperType
            SubType.prototype = new SuperType()
            // 使用字面量添加新方法，会导致上一行代码无效
            SubType.prototype = {
                getSubValue: function () {
                    return this.subProperty
                },
                somOtherMethod: function () {
                    return false
                }
            }
            var instance = new SubType()
            console.warn("instance.getSuperType()", instance)
        }
    </script>
</body>

</html>